﻿namespace TuxedoLib
{
	public class Image
	{
		// These are internal to allow quick access without calling a getter property within the assembly.
		internal int width;
		internal int height;
		internal int[] pixels;
		internal int colorKey;
		internal bool useColorKey;
		internal bool useAlpha;

		public Image(int width, int height)
		{
			this.width = width;
			this.height = height;
			this.pixels = new int[width * height];
			this.useColorKey = false;
			this.useAlpha = true;
		}

		public int Width { get { return this.width; } }
		public int Height { get { return this.height; } }

		public void Fill(byte red, byte green, byte blue, byte alpha)
		{
			if (!useAlpha) alpha = 255;

			int color =
				((int)(alpha & 255) << 24) |
				((int)(red & 255) << 16) |
				((int)(green & 255) << 8) |
				((int)(blue & 255));

			for (int i = pixels.Length - 1; i >= 0; --i)
			{
				pixels[i] = color;
			}
		}

		public void Fill(byte red, byte green, byte blue)
		{
			this.Fill(red, green, blue, 255);
		}

		public void SetColorKey(byte red, byte green, byte blue, byte alpha)
		{
			this.colorKey = (((int)alpha) << 24) | (((int)red) << 16) | (((int)green) << 8) | ((int)blue);
			this.useColorKey = true;
		}

		public void ClearColorKey()
		{
			this.useColorKey = false;
		}

		private const int ALPHA_CHECK = 255 << 24;

		public void Blit(Image image, int targetX, int targetY)
		{
			int targetRight = targetX + image.width - 1;
			int targetBottom = targetY + image.height - 1;
			int sourceXStart = 0;
			int sourceXEnd = image.width - 1;
			int sourceYStart = 0;
			int sourceYEnd = image.height - 1;

			if (targetRight >= this.width)
			{
				int adjustX = targetRight - this.width + 1;
				sourceXEnd -= adjustX;
			}

			if (targetBottom >= this.height)
			{
				int adjustY = targetBottom - this.height + 1;
				sourceYEnd -= adjustY;
			}

			if (targetX < 0)
			{
				sourceXStart = -targetX;
				targetX = 0;
			}

			if (targetY < 0)
			{
				sourceYStart = -targetY;
				targetY = 0;
			}

			if (sourceXStart > sourceXEnd || sourceYStart > sourceYEnd)
			{
				return;
			}

			int sourceIndex, targetIndex;

			int x;
			int copyHeight = sourceYEnd - sourceYStart + 1;
			int copyWidth = sourceXEnd - sourceXStart + 1;
			int sourceWidth = image.width;
			int targetWidth = this.width;

			int[] sourcePixels = image.pixels;
			int[] targetPixels = this.pixels;
			int color;

			if (image.useColorKey)
			{
				int colorKeyValue = image.colorKey;
				for (int y = copyHeight - 1; y >= 0; --y)
				{
					sourceIndex = (sourceYStart + y) * sourceWidth + sourceXStart;
					targetIndex = (y + targetY) * targetWidth + targetX;

					for (x = 0; x < copyWidth; ++x)
					{
						color = sourcePixels[sourceIndex++];
						if (color != colorKeyValue)
						{
							targetPixels[targetIndex++] = color;
						}
						else
						{
							++targetIndex;
						}
					}
				}
			}
			else if (image.useAlpha)
			{
				int originalColor, 
					originalRed, 
					originalGreen, 
					originalBlue, 
					originalAlpha, 
					targetRed, 
					targetGreen,
					targetBlue,
					targetAlpha,
					finalAlpha,
					finalRed,
					finalGreen,
					finalBlue;

				for (int y = copyHeight - 1; y >= 0; --y)
				{
					sourceIndex = (sourceYStart + y) * sourceWidth + sourceXStart;
					targetIndex = (y + targetY) * targetWidth + targetX;

					for (x = 0; x < copyWidth; ++x)
					{
						color = sourcePixels[sourceIndex++];
						if ((ALPHA_CHECK & color) != ALPHA_CHECK)
						{
							if ((ALPHA_CHECK & color) != 0)
							{
								originalColor = targetPixels[targetIndex];
								originalAlpha = (originalColor >> 24) & 255;
								if (originalAlpha != 0)
								{
									targetAlpha = (color >> 24) & 255;
									targetRed = (color >> 16) & 255;
									targetGreen = (color >> 8) & 255;
									targetBlue = color & 255;

									originalRed = (originalColor >> 16) & 255;
									originalGreen = (originalColor >> 8) & 255;
									originalBlue = originalColor & 255;
									if (originalAlpha == 255)
									{
										finalAlpha = 255;
										finalRed = (targetRed * targetAlpha + originalRed * 255) / (targetAlpha + 255);
										finalGreen = (targetGreen * targetAlpha + originalGreen * 255) / (targetAlpha + 255);
										finalBlue = (targetBlue * targetAlpha + originalBlue * 255) / (targetAlpha + 255);
									}
									else
									{
										finalAlpha = 255 - (255 - targetAlpha) * (255 - originalAlpha) / 255;
										finalRed = (targetRed * targetAlpha + originalRed * originalAlpha) / (targetAlpha + originalAlpha);
										finalGreen = (targetGreen * targetAlpha + originalGreen * originalAlpha) / (targetAlpha + originalAlpha);
										finalBlue = (targetBlue * targetAlpha + originalBlue * originalAlpha) / (targetAlpha + originalAlpha);
									}
									color = (finalAlpha << 24) | (finalRed << 16) | (finalGreen << 8) | finalBlue;
								}

								targetPixels[targetIndex++] = color;
							}
							else
							{
								++targetIndex;
							}
						}
						else
						{
							targetPixels[targetIndex++] = color;
						}
					}
				}
			}
			else
			{
				for (int y = copyHeight - 1; y >= 0; --y)
				{
					sourceIndex = (sourceYStart + y) * sourceWidth + sourceXStart;
					targetIndex = (y + targetY) * targetWidth + targetX;

					for (x = 0; x < copyWidth; ++x)
					{
						color = sourcePixels[sourceIndex++];
						targetPixels[targetIndex++] = color;
					}
				}
			}
		}
	}
}
